<script setup>
import { ref, onMounted } from 'vue'
import girlImg from '../../../assets/canvas/girl.jpg'

const canvas = ref(null)
const lineCanvas = ref(null)
const rectCanvas = ref(null)
const textCanvas = ref(null)
const pathCanvas = ref(null)
const imageCanvas = ref(null)
const gradientCanvas = ref(null)
const patternCanvas = ref(null)
const shadowCanvas = ref(null)
const alphaCanvas = ref(null)
const transformCanvas = ref(null)

const blobUrl = ref('')
const base64Url = ref('')

onMounted(() => {
    const context = canvas.value.getContext('2d')

    context.fillStyle = 'orange'
    context.arc(50, 50, 50, 0, 2 * Math.PI)
    context.fill()

    canvas.value.toBlob((blob) => {
        blobUrl.value = URL.createObjectURL(blob)
    })

    base64Url.value = canvas.value.toDataURL()

    // 线条样式 
    const lineContext = lineCanvas.value.getContext('2d')
    lineContext.lineWidth = 10

    lineContext.lineCap = 'butt'
    lineContext.beginPath()
    lineContext.moveTo(20, 10)
    lineContext.lineTo(120, 10)
    lineContext.stroke()

    lineContext.lineCap = 'round'
    lineContext.beginPath()
    lineContext.moveTo(20, 30)
    lineContext.lineTo(120, 30)
    lineContext.stroke()

    lineContext.lineCap = 'square'
    lineContext.beginPath()
    lineContext.moveTo(20, 50)
    lineContext.lineTo(120, 50)
    lineContext.stroke()

    lineContext.lineJoin = 'miter'
    lineContext.beginPath()
    lineContext.moveTo(70, 70)
    lineContext.lineTo(70, 100)
    lineContext.lineTo(40, 100)
    lineContext.stroke()

    lineContext.lineJoin = 'round'
    lineContext.beginPath()
    lineContext.moveTo(160, 70)
    lineContext.lineTo(160, 100)
    lineContext.lineTo(130, 100)
    lineContext.stroke()

    lineContext.lineJoin = 'bevel'
    lineContext.beginPath()
    lineContext.moveTo(250, 70)
    lineContext.lineTo(250, 100)
    lineContext.lineTo(220, 100)
    lineContext.stroke()

    lineContext.lineWidth = 1
    lineContext.setLineDash([5, 10, 20, 30])
    lineContext.lineDashOffset = -5
    lineContext.beginPath()
    lineContext.moveTo(0, 130)
    lineContext.lineTo(300, 130)
    lineContext.stroke()

    // 矩形
    let rectContext = rectCanvas.value.getContext('2d')
    rectContext.strokeRect(20, 20, 100, 50)
    rectContext.fillStyle = 'red'
    rectContext.fillRect(150, 20, 100, 50)
    rectContext.clearRect(50, 30, 150, 30)

    // 文本
    let textContext = textCanvas.value.getContext('2d')
    textContext.fillStyle = 'red'
    textContext.font = '20px bold 微软雅黑'
    textContext.fillText('王海洋正在热身...', 20, 20)
    textContext.font = '40px bold'
    textContext.strokeText('王海洋正在热身...', 20, 70)

    let textObj = textContext.measureText('hcxowe')
    textContext.fillText('hcxowe', 20, 110)

    let textWidth = Math.ceil(textObj.width)
    textContext.font = '12px normal'
    textContext.fillText('宽度' + textWidth, 22 + textWidth, 110)

    // 路径
    let pathContext = pathCanvas.value.getContext('2d')
    pathContext.strokeStyle = 'blue'
    pathContext.fillStyle = 'red'

    pathContext.beginPath()
    pathContext.moveTo(10, 10)
    pathContext.lineTo(110, 10)
    pathContext.lineTo(60, 40)
    pathContext.closePath()
    pathContext.fill()
    pathContext.stroke()

    pathContext.moveTo(0, 100)
    pathContext.bezierCurveTo(50, 0, 150, 200, 200, 100)
    pathContext.stroke()

    pathContext.moveTo(0, 200)
    pathContext.quadraticCurveTo(100, 100, 200, 200)
    pathContext.stroke()

    pathContext.beginPath()
    pathContext.arc(100, 300, 50, 0, Math.PI * 2)
    pathContext.stroke()

    pathContext.beginPath()
    pathContext.moveTo(10, 400)
    pathContext.arcTo(60, 450, 110, 350, 50)
    pathContext.lineTo(210, 400)
    pathContext.stroke()

    pathContext.beginPath()
    pathContext.rect(10, 450, 200, 100)
    pathContext.stroke()

    pathContext.beginPath()
    pathContext.ellipse(110, 620, 100, 50, Math.PI / 6, 0, Math.PI * 2)
    pathContext.stroke()

    pathContext.beginPath()
    pathContext.moveTo(300, 200)
    pathContext.lineTo(400, 200)
    pathContext.lineTo(350, 300)
    pathContext.closePath()
    pathContext.clip()
    pathContext.rect(300, 200, 200, 200)
    pathContext.fill()

    // 图像
    let imgContext = imageCanvas.value.getContext('2d')

    let image = new Image()
    
    image.onload = function() {
        imgContext.drawImage(this, 0, 0, 250, 150)
        
        imgContext.font = '12px 微软雅黑'
        imgContext.fillText('drawImage(data, 0, 0, 250, 150)', 260, 20)
        imgContext.fillText('原始图片500*300,放入(0,0),250*150的区域内', 260, 50)

        imgContext.drawImage(this, 125, 75, 250, 150, 0, 160, 250, 150)
        imgContext.fillText('drawImage(data, 125, 75, 250, 150, 0, 160, 250, 150)', 260, 170)
        imgContext.fillText('原始图片指定位置(125,75)，指定大小250*150, 放入(0,0),250*150的区域内', 260, 200)

        let imageData = imgContext.createImageData(250, 150)
        let sourceImgData = imgContext.getImageData(0, 0, 250, 150)

        let length = sourceImgData.data.length
        for (let index = 0; index < length; index += 4) {
            let r = sourceImgData.data[index]
            let g = sourceImgData.data[index + 1]
            let b = sourceImgData.data[index + 2]

            // 计算灰度
            let gray = r * 0.299 + g * 0.587 + b * 0.114
            sourceImgData.data[index] = gray
            sourceImgData.data[index + 1] = gray
            sourceImgData.data[index + 2] = gray

            if (index / 4 % 2 == 1 && Math.ceil(index / (4 * 250)) % 2 == 1) {
                imageData.data[index] = r
                imageData.data[index + 1] = g
                imageData.data[index + 2] = b
                imageData.data[index + 3] = 256
            }
        }

        imgContext.putImageData(sourceImgData, 0, 330)
        imgContext.putImageData(imageData, 0, 500)

        imgContext.fillText('getImageData(0, 0, 250, 150)', 260, 330)
        imgContext.fillText('获取指定位置(0, 0), 指定大小250*150的图像数据，进行灰度处理', 260, 350)
        imgContext.fillText('putImageData(sourceImgData, 0, 330)', 260, 370)
        imgContext.fillText('将图形数据绘制到指定位置(0, 330)', 260, 390)
        imgContext.fillText('createImageData(250, 150)', 260, 500)
        imgContext.fillText('创建指定大小250*150的图像数据，默认所有像素为透明黑色(0, 0, 0, 0)', 260, 520)

        let canvasCreated = document.createElement('canvas')
        canvasCreated.width = 100
        canvasCreated.height = 60
        canvasCreated.getContext('2d').drawImage(this, 0, 0, 100, 60)

        let patternContext = patternCanvas.value.getContext('2d')
        let pattern = patternContext.createPattern(canvasCreated, 'repeat')
        patternContext.fillStyle = pattern
        patternContext.fillRect(0, 0, 600, 300)
    }

    image.src = girlImg

    let gradientContext = gradientCanvas.value.getContext('2d')

    let linerGradient = gradientContext.createLinearGradient(0, 0, 500, 0)
    linerGradient.addColorStop(0.0, "red")
    linerGradient.addColorStop(0.25, "yellow")
    linerGradient.addColorStop(0.5, "green")
    linerGradient.addColorStop(0.75, "blue")
    linerGradient.addColorStop(1.0, "purple")

    gradientContext.fillStyle = linerGradient
    gradientContext.fillRect(0, 0, 600, 100)
    gradientContext.fillRect(10, 110, 150, 100)
    gradientContext.fillRect(210, 110, 150, 100)
    gradientContext.fillRect(410, 110, 150, 100)

    let radialGradient = gradientContext.createRadialGradient(250, 400, 0, 250, 400, 100)
    radialGradient.addColorStop(0.0, "red")
    radialGradient.addColorStop(0.25, "yellow")
    radialGradient.addColorStop(0.5, "green")
    radialGradient.addColorStop(0.75, "blue")
    radialGradient.addColorStop(1.0, "purple")

    gradientContext.fillStyle = radialGradient
    gradientContext.fillRect(0, 300, 600, 300)

    // 阴影
    let shadowContext = shadowCanvas.value.getContext('2d')
    shadowContext.shadowColor = 'red'
    shadowContext.shadowBlur = 5
    shadowContext.shadowOffsetX = 10
    shadowContext.shadowOffsetY = 10

    shadowContext.strokeRect(20, 20, 100, 50)

    shadowContext.font = '24px bold'
    shadowContext.fillText('王海洋正在热身...', 200, 40)

    // 透明
    let alphaContext = alphaCanvas.value.getContext('2d')

    alphaContext.beginPath()
    alphaContext.fillStyle = 'red'
    alphaContext.globalAlpha = '1'
    alphaContext.arc(75, 50, 25, 0, Math.PI * 2)
    alphaContext.fill()

    alphaContext.beginPath()
    alphaContext.fillStyle = 'blue'
    alphaContext.globalAlpha = '0.5'
    alphaContext.arc(60, 75, 25, 0, Math.PI * 2)
    alphaContext.fill()

    alphaContext.beginPath()
    alphaContext.fillStyle = 'green'
    alphaContext.globalAlpha = '0.5'
    alphaContext.arc(85, 75, 25, 0, Math.PI * 2)
    alphaContext.fill()

    // 变换
    let transformContext = transformCanvas.value.getContext('2d')
    transformContext.rotate(45 * Math.PI / 180) // 左边旋转45度
    
    transformContext.fillRect(80, -20, 100, 50)
    transformContext.fillText('我被旋转了45度', 150, -40)

    // 重置坐标
    transformContext.setTransform(1, 0, 0, 1, 0, 0)

    transformContext.fillRect(20, 200, 50, 50)
    transformContext.fillText('原图形', 20, 260)
    transformContext.fillText('scale(3, 2)之后', 280, 260)

    transformContext.scale(3, 2)
    transformContext.fillRect(40, 100, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)

    transformContext.font = '20px bold'
    transformContext.scale(1, -1)
    transformContext.fillText('scale(1, -1)之后我被垂直翻转了', 280, -270)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)

    transformContext.font = '14px bold'
    transformContext.fillText('以正方形中心旋转并放大两倍', 180, 420)
    transformContext.translate(75, 425)
    transformContext.rotate(45 * Math.PI / 180)
    transformContext.scale(2, 2)
    transformContext.translate(-75, -425)
    transformContext.fillRect(50, 400, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)

    transformContext.fillRect(50, 600, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 100, 50)

    transformContext.textAlign = 'center'
    transformContext.fillRect(50, 600, 50, 50)
    transformContext.fillText('setTransform(1, 0, 0, 1, 100, 50)表示translate(100, 50)', 100, 680)

    transformContext.setTransform(3, 0, 0, 2, 0, 0)
    transformContext.fillRect(80, 300, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)
    transformContext.fillText('setTransform(3, 0, 0, 2, 0, 0)表示scale(3, 2)', 300, 590)

    transformContext.setTransform(Math.cos(45), Math.sin(45), -Math.sin(45), Math.cos(45), 50, 800)
    transformContext.fillRect(0, 0, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)
    transformContext.textAlign = 'left'
    transformContext.fillText('setTransform(Math.cos(45), Math.sin(45), -Math.sin(45), Math.cos(45), 50, 800)表示rotate(45 * Math.PI / 180)', 20, 900)

    transformContext.setTransform(1, 0, Math.tan(45), 1, 50, 950)
    transformContext.fillRect(0, 0, 50, 50)
    transformContext.setTransform(1, Math.tan(45), 0, 1, 250, 950)
    transformContext.fillRect(0, 0, 50, 50)
    transformContext.setTransform(1, Math.tan(45), Math.tan(45), 1, 400, 950)
    transformContext.fillRect(0, 0, 50, 50)

    transformContext.setTransform(1, 0, 0, 1, 0, 0)
    transformContext.textAlign = 'left'
    transformContext.fillText('setTransform(1,tan(θy),tan(θx),1,0,0)表示skew(θx, θy)', 20, 1100)
})
</script>

<template>
    <div class="wrapper">
        <hr class="hr-solid-content" data-content="width or height">
        <canvas ref="canvas" width="100" height="100" style="height: 50px;"></canvas>
        <p><code>{{ `<canvas width="100" height="100" style="height: 50px;"></canvas>` }}</code></p>
        <ul style="list-style: number;">
            <li>canvas的width*height默认300*150</li>
            <li>canvas的width与height属性只能生效为整数</li>
            <li>CSS设置的width，height权重大于canvas的height属性</li>
            <li>如果CSS只设置width或者height，canvas会强制保持width与height属性值的等比缩放</li>
            <li>canvas绘制与CSS的width&height无关</li>
        </ul>

        <hr class="hr-solid-content" data-content="getContext()获取canvas绘制上下文">
        <p><code>{{ `let context = canvas.getContext('2d')` }}</code></p>
        <p>所有绘制活动都需要通过上下文进行</p>

        <hr class="hr-solid-content" data-content="toBlob()获取canvas图像对应的Blob对象">
        <img :src="blobUrl" />
        <p><code>{{  `canvas.toBlob((blob) => { let url = URL.createObjectURL(blob) })` }}</code></p>
        <p>把canvas图像转化为二进制数据可以用于上传，或者直接通过URL.createObjectURL()转资源标识直接img展示</p>

        <hr class="hr-solid-content" data-content="toDataURL()获取canvas图像的base64数据">
        <img :src="base64Url" />
        <p><code>{{ `let base64Url = canvas.toDataURL(mimeType, quality)` }}</code></p>

        <hr class="hr-solid-content" data-content="Canvas绘制">

        <p class="title">填充和描边</p>
        <ol>
            <li>fillStyle 填充样式，默认#000，支持渐变，纹理</li>
            <li>fill(fillRule) 填充；填充规则： nonzero非零规则， evenodd：奇偶规则</li>
            <li>strokeStyle 描边样式，默认#000，支持渐变，图案</li>
            <li>stroke() 描边</li>
        </ol>
        
        <p class="title">文本样式</p>
        <ol>
            <li>font 设置字体相关样式，包括字号，字体信息。默认值是10px sans-serif</li>
            <li>textAlign 设置文本水平对齐方式。支持属性值有：start（默认值），end，left，right以及center</li>
            <li>textBaseline 设置文本基线对齐方式。支持属性值有：top，hanging，middle，alphabetic（默认值），ideographic，bottom</li>
            <li>direction 设置文本显示方向。支持属性值有：inherit（默认值），ltr和rtl</li>
        </ol>

        <p class="title">线条样式</p>
        <ol>
            <li>lineWidth 线条宽度：默认1，支持小数</li>
            <li>lineCap 线条端点样式：butt(默认，无端帽)，round(圆形端帽)，square(方形端帽)</li>
            <li>lineJoin 线条转角样式：miter(默认，尖角)，round(圆角)，bevel(平角)</li>
            <li>miterLimit 尖角限制比率，默认10</li>
            <li>setLineDash([]) 设置线条为虚线，参数为虚实线长度的数组</li>
            <li>lineDashOffset 设置虚线起始点偏移</li>
            <li>getLineDash() 获取虚线数值，返回偶数个数的数组</li>
        </ol>
        <canvas ref="lineCanvas"></canvas>

        <p class="title">矩形绘制</p>
        <ol>
            <li>clearRect()  清除指定矩形区域内部所有的像素信息为初始色（通常为透明）</li>
            <li>fillRect()   矩形填充，可以填充颜色，渐变，图案等</li>
            <li>strokeRect() 矩形描边</li>
        </ol>
        <canvas ref="rectCanvas"></canvas>

        <p class="title">文本绘制</p>
        <ol>
            <li>fillText(text, x, y, maxWidth)   文字填充，可以填充纯色，渐变或者图案</li>
            <li>strokeText(text, x, y, maxWidth) 文字描边</li>
            <li>measureText(text) 文字测量。返回TextMetrics对象，该对象的width属性值就是字符占据的宽度</li>
        </ol>
        <canvas ref="textCanvas"></canvas>
        
        <p class="title">路径绘制</p>
        <ol>
            <li>beginPath() 开始一个新路径</li>
            <li>closePath() 闭合一个路径</li>
            <li>moveTo(x, y) 路径绘制起始点</li>
            <li>lineTo(x, y) 绘制直线到指定坐标点</li>
            <li>bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 绘制贝赛尔曲线到指定坐标点</li>
            <li>quadraticCurveTo(cpx, cpy, x, y) 绘制二次贝赛尔曲线到指定坐标点</li>
            <li>arc(x, y, radius, startAngle, endAngle [, anticlockwise]) 绘制圆弧</li>
            <li>arcTo(cp1x, cp1y, cp2x, cp2y, radius) 绘制圆弧，和之前的点以直线相连</li>
            <li>rect(x, y, width, height) 绘制矩形路径</li>
            <li>ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) 绘制椭圆路径</li>
            <li>clip(fillRule) 创建剪裁路径，之后绘制的路径只有在里面的才会显示</li>
        </ol>
        <canvas ref="pathCanvas" width="500" height="700"></canvas>

        <p class="title">图片与像素</p>
        <ol>
            <li>
                <div>drawImage(image, dx, dy) 指定位置绘制图像，保持图像高宽</div>
                <div>drawImage(image, dx, dy, dWidth, dHeight) 在指定区域绘制图像，图像会拉伸适应区域</div>
                <div>drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 裁剪图像展示在指定区域</div>
            </li>
            <li>createImageData(width, height) 创建一个全新的空的ImageData对象。该对象中的所有像素信息都是透明黑</li>
            <li>getImageData(sx, sy, sWidth, sHeight) 返回 ImageData 对象，其中包含Canvas画布部分或完整的像素点信息</li>
            <li>
                <div>putImageData(imagedata, dx, dy) 将给定ImageData对象的数据绘制到指定坐标上</div>
                <div>putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) 将给定ImageData对象的数据裁剪并绘制到指定坐标上</div>
            </li>
        </ol>
        <canvas ref="imageCanvas" width="700" height="700"></canvas>

        <p class="title">渐变相关</p>
        <ol>
            <li>
                <div>createLinearGradient(x0, y0, x1, y1) 从起点到终点的渐变，渐变坐标为全局。</div>
                <div>addColorStop(pos, color) 添加颜色点，pos范围0-1</div>
            </li>
            <li>
                <div>createRadialGradient(x0, y0, r0, x1, y1, r1) 径向渐变，参数为两个圆环坐标</div>
                <div>addColorStop(pos, color) 添加颜色点，pos范围0-1</div>
            </li>
        </ol>
        <canvas ref="gradientCanvas" width="600" height="500"></canvas>

        <p class="title">图案相关</p>
        <ol>
            <li>
                <div>createPattern(image, repeat) 用来创建图案对象。可以指定平铺方式, image可以是以下几种：</div>
                <div>{{ `HTMLImageElement，也就是<img>元素。` }}</div>
                <div>{{ `HTMLVideoElement，也就是<video>元素，例如捕获摄像头视频产生的图像信息。` }}</div>
                <div>HTMLCanvasElement</div>
                <div>CanvasRenderingContext2D</div>
                <div>ImageBitmap</div>
                <div>ImageData</div>
                <div>Blob</div>
                <div>repeat: 默认'repeat'，'repeat-x'， 'repeat-y'，'no-repeat'</div>
            </li>
        </ol>
        <canvas ref="patternCanvas" width="600" height="300"></canvas>

        <p class="title">阴影相关</p>
        <ol>
            <li>shadowBlur 指定阴影的模糊程度。默认值是0，表示不模糊</li>
            <li>shadowColor 指定阴影的颜色。默认值是透明黑</li>
            <li>shadowOffsetX 水平偏移的大小，数值，默认值是0</li>
            <li>shadowOffsetY 垂直偏移的大小，数值，默认值是0</li>
        </ol>
        <canvas ref="shadowCanvas" width="600" height="100"></canvas>

        <p class="title">位置检测</p>
        <ol>
            <li>isPointInPath(x, y, fillRule) 用来检测某个点是否在当前路径中, 当我们每次执行一次beginPath()检测路径就变成了这次beginPath()之后绘制的路径,原来的路径不参检测。</li>
            <li>isPointInStroke(x, y) 用来检测对应的点是否在描边路径上，描边越粗，则检测区域越大。</li>
        </ol>
        
        <p class="title">透明度和层级</p>
        <ol>
            <li>globalAlpha 设置的全局透明度，范围是0到1, 0完全透明，1不透明</li>
            <li>
                <div>globalCompositeOperation 可以用来设置Canvas图形的混合模式。可以衍生很多其他效果，以及改变绘制图形的上下层叠关系。例如遮罩, 剪裁</div>
                <div>source-over 默认混合方式，直接在现有图形的上方绘制，纯视觉覆盖</div>
                <div>source-in 仅在和原Canvas图形重叠的位置绘制新图形，否则处理为透明。如果重叠位置是半透明颜色，则也处理为半透明</div>
                <div>source-out 和source-in相反，重叠的位置是透明的</div>
                <div>source-atop 仅在新内容与原内容重叠的位置进行类似遮罩的绘制，如果是没有重叠的位置，则原封不动显示</div>
                
                <div>destination-over 新内容在原内容的下方绘制, 可以改变层级关系</div>
                <div>destination-in 显示原内容和新内容重叠的部分</div>
                <div>destination-out 隐藏原内容和新内容重叠的部分</div>
                <div>destination-atop 原内容只显示和新内容重叠的部分，同时新内容在下方显示</div>
                <div>lighter 自然光混合效果。红绿蓝混合会成为白色。其色值混合原理如下，比较新内容和原内容颜色的所有通道值的总和，并显示更高值的颜色</div>
                <div>copy 只显示新内容</div>
                <div>xor 互相重叠的区域是透明的</div>
                <div>multiply 正片叠底。顶层的像素与底层的对应像素相乘。结果是一幅更黑暗的图画。</div>
                <div>screen 滤色。像素反转，相乘，然后再反转。最终得到更淡的图形（和multiply相反）</div>
                <div>overlay 叠加。multiply和screen组合效果。基础图层上暗的部分更暗，亮的部分更亮。</div>
                <div>darken 变暗。保留原内容和新内容中最暗的像素。</div>
                <div>lighten 变亮。保留原内容和新内容中最亮的像素。</div>
                <div>color-dodge 颜色减淡。底部图层色值除以顶部图层的反相色值。最终的颜色值是：底色+（底色*顶色）/（255-顶色）</div>
                <div>color-burn 颜色加深。底部图层的色值除以顶部图层色值，得到的结果再反相。</div>
                <div>hard-light 强光。类似overlay，是multiply和screen组合效果。只不过底层和顶层位置交换下</div>
                <div>soft-light 柔光。hard-light的柔和版本。纯黑色或白色不会生成为纯黑色或白色</div>
                <div>difference 差异。顶层色值减去底层色值的绝对值。如果都是白色，则最后是黑色，因为值为0</div>
                <div>exclusion 排除。类似difference，不过对比度较低</div>
                <div>hue 色调。最终的颜色保留底层的亮度和色度，同时采用顶层的色调。</div>
                <div>saturation 饱和度。最终的颜色保留底层的亮度和色调，同时采用顶层的色度。</div>
                <div>color 色值。最终的颜色保留底层的亮度，同时采用顶层的色调和色度。</div>
                <div>luminosity 亮度。最终的颜色保留底层的色调和色度，同时采用顶层的亮度</div>
            </li>
        </ol>
        <canvas ref="alphaCanvas" width="600" height="150"></canvas>

        <p class="title">变换</p>
        <ol>
            <li>rotate(angle)
                <div>Canvas画布坐标系旋转的角度，单位是弧度。注意，此旋转和CSS3的旋转变换不一样，旋转的是坐标系，而非元素。因此，实际开发的时候，旋转完毕，需要将坐标系再还原。</div>
                <div>默认旋转中心点是Canvas的左上角(0, 0)坐标点，如果希望改变旋转中心点，例如以Canvas画布的中心旋转，需要先使用translate()位移旋转中心点。</div>
            </li>
            <li>
                <div>scale(x, y) x, y 表示水平垂直缩放的比例，-1表示翻转</div>
                <div>用来缩放Canvas画布的坐标系，只是影响坐标系，之后的绘制会受此方法影响，但之前已经绘制好的效果不会有任何变化。</div>
            </li>
            <li>translate(x, y) 对Canvas坐标系进行整体位移, 实际开发常用来改变其它变换方法的变换中心点</li>
            <li>transform(a, b, c, d, e, f) 通过矩阵进一步对当前坐标系进行变换</li>
            <li>setTransform(a, b, c, d, e, f) 通过矩阵重置坐标系
                <div>matrix(1, 0, 0, 1, x, y) 平移公式</div>
                <div>matrix(scaleX, 0, 0, scaleY, 0, 0) 缩放公式</div>
                <div>matrix(cosθ, sinθ, -sinθ, cosθ, 0, 0) 旋转公式</div>
                <div>matrix(1, tan(θy), tan(θx), 1, 0, 0) 拉伸公式</div>
            </li>
        </ol>
        <canvas ref="transformCanvas" width="800" height="1200"></canvas>

        <p class="title">Canvas状态</p>
        <ol>
            <li>save() 保存当前Canvas画布状态并放在栈的最上面，可以使用restore()方法依次取出。
                <div>只保存绘画状态：当前矩阵变换；裁剪区域；虚线设置；</div>
                <div>以及strokeStyle，fillStyle，globalAlpha，lineWidth，lineCap，lineJoin，miterLimit，lineDashOffset，shadowOffsetX，shadowOffsetY，shadowBlur，shadowColor，globalCompositeOperation，font，textAlign，textBaseline。</div>
            </li>
            <li>
                <div>restore()</div>
                <div>依次从堆栈上方弹出存储的canvas存储状态</div>
            </li>
            <li>canvas 返回当前上下文来自的Canvas元素</li>
        </ol>
        
        <p class="title">其他方法</p>
        <ol>
            <li>drawFocusIfNeeded(element) 如果给定元素被聚焦，则该方法在当前路径周围绘制焦点环。</li>
            <li>scrollPathIntoView() 将当前路径或给定路径滚动到视图中。</li>
        </ol>

        <p class="title">滤镜</p>
        <ol>
            <li>filter 给canvas图像添加滤镜</li>
            <li>
                <div>滤镜函数:</div>
                <div>blur(5px) 模糊</div>
                <div>brightness(2.4), 亮度</div>
                <div>contrast(200%), 对比度</div>
                <div>drop-shadow(4px 4px 8px blue), 投影</div>
                <div>grayscale(50%), 灰度</div>
                <div>hue-rotate(90deg), 色调</div>
                <div>invert(75%), 反相</div>
                <div>opacity(25%), 透明度</div>
                <div>saturate(230%), 饱和度</div>
                <div>sepia(60%), 褐色</div>
                <div>url(#someId), SVG滤镜id</div>
            </li>
        </ol>
    </div>
</template>

<style scoped>
.wrapper {
    position: relative;
    height: 100%;
    padding: 8px;
    overflow: auto;
}

canvas {
    border: 1px solid #ccc;
}

.title {
    font-weight: bold;
}
</style>